home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscClipTextPalette / MiscClipText.subproj / MiscClipTFCell.m < prev    next >
Encoding:
Text File  |  1995-07-13  |  13.5 KB  |  517 lines

  1. //
  2. //    MiscClipTextFieldCell.h -- a cell for displaying long string values
  3. //        Written and Copyright (c) 1995 by Balazs Pataki. 
  4. //                Version 1.0.  All rights reserved.
  5. //
  6. //        This notice may not be removed from this source code.
  7. //
  8. //    This object is included in the MiscKit by permission from the author
  9. //    and its use is governed by the MiscKit license, found in the file
  10. //    "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  11. //    for a list of all applicable permissions and restrictions.
  12. //    
  13.  
  14. #import <appkit/appkit.h>
  15. #import    <misckit/MiscString.h>
  16. #import <objc/objc-runtime.h>
  17. #import "MiscClipTextFieldCell.h"
  18.  
  19.  
  20. #define    CLASS_NAME                "MiscClipTextFieldCell"
  21. #define    CLASS_VERSION            1
  22.  
  23.  
  24. #define    DEFAULTCLIPPER            "..."
  25. #define DELIMITERS                [delimiters stringValue]
  26.  
  27.  
  28. // To make texts displayed in cells looking good inside views, the maximum 
  29. // width of a cell is 4.0 points (o whatever) less than the width of the view
  30. // the cell is displayed in. So, we need this little adjustment (am I right? 
  31. // - maybe not but at least it works) 
  32. #define CELL_ADJUSTMENT            4.0
  33. #define BEZELED_ADJUSTMENT        5.0
  34. #define    BORDERED_ADJUSTMENT        4.0
  35.  
  36.  
  37. #define FLUSH    [[[control window] reenableFlushWindow] flushWindow];
  38.  
  39.  
  40. @interface MiscClipTextFieldCell (Private)
  41. - _clipStringValue:(const char *)aString;
  42. - _copyClipperObj:obj fromZone:(NXZone *)zone;
  43. - _copyDelimitersObj:obj fromZone:(NXZone *)zone;
  44. - _copyFullStringObj:obj fromZone:(NXZone *)zone;
  45. @end
  46.  
  47.  
  48. /*
  49.                         ********************************
  50.                         *                              *
  51.                         *    MiscClipTextFieldCell     *
  52.                         *                              *
  53.                         ********************************
  54. */
  55.  
  56. @implementation MiscClipTextFieldCell
  57.  
  58. + initialize
  59. // Set class version
  60. {
  61.     if (self == objc_lookUpClass(CLASS_NAME))  {
  62.         [self setVersion:CLASS_VERSION];
  63.     }
  64.     return self;
  65. }
  66.  
  67. - init;
  68. // Initializes a newly allocated MiscClipTextFieldCell with default values, 
  69. // that is the clipper string to "...", uses no delimiters when clipping, and
  70. // clipping happens on the right. The cell's other attributes are set for
  71. // displaying text only (no scroll, selection allowed).  
  72. {
  73.     return [self  initTextCell:""];    
  74. }
  75.  
  76. - initTextCell:(const char*)aString;
  77. {
  78.     [super initTextCell:aString];
  79.  
  80.     clipper = [[MiscString allocFromZone:[self zone]] 
  81.                            initString:DEFAULTCLIPPER];
  82.     fullString = [[MiscString allocFromZone:[self zone]] init];
  83.     delimiters  = nil;                /* As a deafult we don't have delimiters*/
  84.     clipOnRight = YES;                /*    and clipping happens on the right    */
  85.     clipEnabled = YES;
  86.     
  87.     [self setDelegate:self];
  88.     // MiscClipField is initialized for only displaying its string value 
  89.     [self setBezeled:YES];
  90.     [self setEditable:NO];
  91.     [self setScrollable:NO];
  92.     [self setBackgroundGray:NX_LTGRAY];
  93.  
  94.     return self;
  95. }
  96.  
  97. - copyFromZone:(NXZone *)zone
  98. {
  99.     id obj = [super copyFromZone:zone];    
  100.     
  101.     [obj _copyClipperObj:clipper fromZone:zone];
  102.     [obj _copyDelimitersObj:delimiters fromZone:zone];
  103.     [obj _copyFullStringObj:fullString fromZone:(NXZone *)zone];
  104.     
  105.     return obj;
  106. }
  107.  
  108. - free
  109. {
  110.     [fullString free];
  111.     [clipper free];
  112.     if (delimiters)
  113.         [delimiters free];
  114.     
  115.     return [super free];
  116. }
  117.  
  118.  
  119. - setDelegate:anObject
  120. {
  121.     delegate = anObject;
  122.     return self;
  123. }
  124.  
  125.  
  126.  
  127. - setClipOnRight:(BOOL) flag
  128. // If flag is YES clipping happens on the right, otherwise on the left of the 
  129. // string in the cell
  130. {
  131.     clipOnRight = flag;
  132.  
  133.     return self;
  134. }
  135.  
  136.  
  137. - setClipperString:(const char*) aString
  138. // Sets  `aString' as the string that is displayed in place of the clipped part
  139. // of the original string
  140. {
  141.     if (clipper)
  142.         [clipper setStringValue:aString];
  143.     else
  144.         clipper = [MiscString newWithString:aString];
  145.  
  146.     return self;
  147. }
  148.  
  149.  
  150. - setClipDelimiters:(const char*) delimChars
  151. // Sets  `delimChars' as delimiters by which the clipping has to happen
  152. {
  153.     if (delimiters) 
  154.         [delimiters setStringValue:delimChars];
  155.     else
  156.         delimiters = [MiscString newWithString:delimChars];
  157.  
  158.     return self;
  159. }
  160.  
  161. - setClipEnabled:(BOOL) flag
  162. // Sets whether the next `setStringValue:' message should clip the text or not 
  163. {
  164.     clipEnabled = flag;
  165.     return self;
  166. }
  167.  
  168.  
  169. - setStringValue:(const char *)aString
  170. {
  171.     if ([fullString cmp:aString] != 0)
  172.         [fullString setStringValue:aString];
  173.  
  174.     if ( clipEnabled )
  175.         return [self _clipStringValue:aString];
  176.     
  177.     return [super setStringValue:aString];
  178.     
  179. }
  180.  
  181. - takeStringValueFrom:sender
  182. {
  183.     [self setStringValue:[sender stringValue]];
  184.     return self;
  185. }
  186.  
  187. - resetStringValue:sender
  188. {
  189.     if ( clipEnabled )
  190.         return [self _clipStringValue:[self fullStringValue]];
  191.     
  192.     return [super setStringValue:[self fullStringValue]];
  193. }
  194.  
  195.  
  196. - (const char*) fullStringValue { return [fullString stringValue]; }
  197. - (BOOL) isClipEnabled            { return clipEnabled; }
  198. - (BOOL) doesClipOnRight        { return clipOnRight; }
  199. - clipper        { return clipper;     }
  200. - delimiters    { return delimiters; }    
  201. - delegate        { return delegate;     }
  202.  
  203. - (BOOL) isWrapped
  204. // Returns yes if the Cell wraps the text by word
  205. {
  206.     return ( cFlags2.noWrap ? NO : YES );
  207. }
  208.  
  209.  
  210. - write:(NXTypedStream *)stream
  211. {
  212.     [super write:stream];
  213.     
  214.     NXWriteObject(stream, fullString);
  215.     NXWriteObject(stream, clipper);
  216.     NXWriteObject(stream, delimiters);
  217.     NXWriteObject(stream, delegate);
  218.     NXWriteType(stream, @encode(BOOL), &clipOnRight);
  219.     NXWriteType(stream, @encode(BOOL), &clipEnabled);
  220.    
  221.     return self;
  222. }
  223.  
  224.  
  225. - read:(NXTypedStream *)stream
  226. {
  227.     [super read:stream];
  228.    
  229.     fullString = NXReadObject(stream);
  230.     clipper    = NXReadObject(stream);
  231.     delimiters = NXReadObject(stream);
  232.     delegate   = NXReadObject(stream);
  233.     NXReadType(stream, @encode(BOOL), &clipOnRight);
  234.     NXReadType(stream, @encode(BOOL), &clipEnabled);
  235.  
  236.     return self;
  237. }
  238.  
  239.  
  240. - awake
  241. // Does nothing but with speed :-)
  242. {
  243.     return self;
  244. }
  245.  
  246.  
  247.  
  248. @end
  249.  
  250.  
  251. /*
  252.                         ********************************
  253.                         *                              *
  254.                         *   MiscClipTextField(Private) *
  255.                         *                              *
  256.                         *    -- Private Methods --     *
  257.                         *                              *
  258.                         ********************************
  259. */
  260.  
  261.  
  262. @implementation MiscClipTextFieldCell (Private)
  263.  
  264. - _clipStringValue:(const char *)aString
  265. // A very long story...
  266. // 
  267. {
  268.     id    control     =     nil;
  269.     id    temp      =     nil;
  270.     id     mainFont =     [self font];
  271.     id     scrnFont =     [mainFont screenFont];                            
  272.     id     theFont  =     (scrnFont ? scrnFont : mainFont); // try to use screen font
  273.     float     clipperWidth =    0; 
  274.     float     clippedWidth =    0;
  275.     float     cellWidth     =  0;
  276.     BOOL    doClip         = NO;
  277.     BOOL      usingDelimiters = NO;
  278.     NXRect    biggestRect;
  279.     NXSize    size;
  280.     int        i=0;
  281.     
  282.     if (!aString) 
  283.         return self;
  284.     
  285.                                             /* More initializing            */        
  286.     if (![clipper stringValue])
  287.         [clipper setStringValue:DEFAULTCLIPPER];
  288.  
  289.     clipperWidth =    [theFont getWidthOf:[clipper stringValue]];     
  290.  
  291.     temp = [[MiscString alloc] initString:aString];
  292.  
  293.     usingDelimiters = ( delimiters && ![delimiters emptyString] ? YES : NO );
  294.     
  295.     // Figure out the initial cell width (the biggest possible cell width)
  296.     // [and do some necessary adjustment too]
  297.     control = [self controlView];
  298.                                         /* Matrix will respond ...            */     
  299.     if ( [control respondsTo:@selector(getCellSize:)] ) {
  300.         NXSize    size;
  301.         [control getCellSize:&size];
  302.         cellWidth = size.width;
  303.         biggestRect.size.width = size.width;
  304.         biggestRect.size.height = size.height;
  305.     }
  306.     else {
  307.         [control getFrame:&biggestRect];
  308.         cellWidth =    NX_WIDTH(&biggestRect);
  309.     }
  310.     
  311.     cellWidth -= CELL_ADJUSTMENT;
  312.     
  313.     if ([self isBezeled])
  314.         cellWidth -= BEZELED_ADJUSTMENT;
  315.     else if ([self isBordered])
  316.         cellWidth -= BORDERED_ADJUSTMENT;
  317.     
  318.     // Configuring the cell
  319.     [self setSelectable:NO];        /* Disable selecting and scrolling        */
  320.     [self setScrollable:NO];
  321.  
  322.     [[control window] disableFlushWindow];
  323.  
  324.     // Check whether we need to clip or not. If we do give delegate an
  325.     // opportunity to clip the string in his custom way. If `temp's string
  326.     // value is still long we do the real clipping. I know its not really
  327.     // sexy doing this with a for loop but ...
  328.     for (i=1; i<=2; i++) {
  329.         doClip = NO;
  330.         
  331.         if ([theFont getWidthOf:[temp stringValue]] > cellWidth)    
  332.               doClip = YES;            /* aString is too wide so do the clip    */
  333.         else {                        /* Try the "trick"                         */
  334.                                     /* Set the clipped string value         */
  335.             [super setStringValue:[temp stringValue]];    
  336.                                     /* Calc how much space it needed to be    */
  337.                                     /*  displayed                            */
  338.             [self calcCellSize:&size inRect:&biggestRect];
  339.                                     /* It didn't fit so its broken into many*/
  340.                                     /* lines (ie: the cell got higher)         */
  341.             if ((size.height > NX_HEIGHT(&biggestRect)))
  342.                 doClip = YES;        /* Do the clip                            */
  343.         }
  344.         if (i==1 && doClip)
  345.             [delegate stringWillBeClipped:temp];
  346.     }
  347.     
  348.     
  349.     // Clip on the right if `aString' is too long
  350.     if ( doClip && clipOnRight ) {
  351.         BOOL cutMore=NO;    // This becomes true if text doesn't fits into 
  352.                             // the cell in the first round 
  353.                         /* The loop cuts characters from the end and checks    */
  354.                         /*    wheter it's short enough to be displayed        */
  355.         while (YES) {            
  356.             clippedWidth=[theFont getWidthOf:[temp stringValue]];
  357.                                 /* If the string is still too long            */
  358.             if ( ((clippedWidth+clipperWidth) >= cellWidth) || cutMore)  {
  359.                 [temp removeFrom:[temp length]-1 length:1];
  360.                 if ( usingDelimiters ) {
  361.                                 /* No delimiter found means we reached the     */
  362.                                 /*    last delimited string element and it     */
  363.                                 /*  can't be shortened anymore because there*/
  364.                                 /*    is no "next" delimter backwards            */
  365.                     int from = [temp rspotOfChars:DELIMITERS];
  366.                     if ( from == -1 )
  367.                         [temp replace:[temp stringValue] with:""];
  368.                     else
  369.                         [temp removeFrom:from+1 length:[temp length]-1];
  370.                 }
  371.                                 /* Last element reached and still long        */
  372.                 if ([temp emptyString]) {
  373.                     [temp setStringValue:[clipper stringValue]];
  374.                     FLUSH;
  375.                     return [super setStringValue:[temp stringValueAndFree]];
  376.                 }
  377.                 cutMore = NO;
  378.             } //End if (still too long)
  379.             else {
  380.                 [temp concatenate:clipper];
  381.                                     /* Set the clipped string value         */
  382.                 [super setStringValue:[temp stringValue]];    
  383.                                     /* Calc how much space it needed to be    */
  384.                                     /*  displayed                            */
  385.                 [self calcCellSize:&size inRect:&biggestRect];
  386.                                     /* It didn't fit so its broken into two */
  387.                                     /* lines (ie: the cell got higher)         */
  388.                 if ( size.height > NX_HEIGHT(&biggestRect) ){
  389.                     cutMore = YES;    /* Force cutting some more                */
  390.                                     /* Remove clipper string from end        */
  391.                     [temp removeFrom:([temp length]-[clipper length]) 
  392.                           length:[clipper length]];
  393.                 }            
  394.                 else {            /* Finished, string value can be displyed    */
  395.                     [temp free];
  396.                     FLUSH;
  397.                     return self;
  398.                 }
  399.             } // End else (check if string value can really be displayed)
  400.         } // End of string shortening loop
  401.     }//End if (clipOnRight)
  402.  
  403.  
  404.  
  405.     // Clip on the left if `aString' is too long
  406.     // (This code is nearly identical to the right clipping one, except that
  407.     //  it scans the string from left to right. Can you see any way to merge 
  408.     //  these two in a meaningful way?) 
  409.     if ( doClip && !clipOnRight ) {
  410.         BOOL cutMore=NO;    // This becomes true if text doesn't fits into 
  411.                             // the cell in the first round         
  412.                         /* The loop cuts characters from the beg. of temp    */
  413.                         /*    and checks    wheter it's short enough to be         */
  414.                         /*    displayed                                        */
  415.         while ( YES ) {
  416.                         // This sometimes lies, maybe because of the screenfont
  417.                         // - printerfont differency
  418.             clippedWidth=[theFont getWidthOf:[temp stringValue]];
  419.                                 /* If string is still too long    or we are    */
  420.                                 /*    forced to cut more ...                    */
  421.             if ( ((clippedWidth+clipperWidth) >= cellWidth) || cutMore)  {
  422.                 [temp removeFrom:0 length:1];
  423.                 if ( usingDelimiters ) {
  424.                     int to=[temp spotOfChars:DELIMITERS];
  425.                                 /* No delimiters found means we reached the */
  426.                                 /*    last delimited string element and it     */
  427.                                 /*  can't be shortened anymore because there*/
  428.                                 /*    is no "next" delimter forward            */
  429.                     if (to == -1)
  430.                         [temp replace:[temp stringValue] with:""];
  431.                     else
  432.                         [temp removeFrom:0 to:to-1];
  433.                 }
  434.                                 /* Last element reached and still long        */
  435.                 if ([temp emptyString]) {
  436.                     [temp setStringValue:[clipper stringValue]];
  437.                     FLUSH;
  438.                     return [super setStringValue:[temp stringValueAndFree]];
  439.                 }
  440.                 cutMore = NO;    /* Turn forcing cutting more off            */
  441.             } //Enf if (still too long)
  442.             else {    
  443.                                     /* Insert clipper                        */
  444.                 [temp insertString:clipper];
  445.                                     /* Set the clipped string value         */
  446.                 [super setStringValue:[temp stringValue]];    
  447.                                     /* Calc how much space it needed to be    */
  448.                                     /*  displayed                            */
  449.                 [self calcCellSize:&size inRect:&biggestRect];
  450.                 
  451.                 if ( size.height > NX_HEIGHT(&biggestRect) ){
  452.                                     /* It didn't fit so its broken into many*/
  453.                                     /* lines (ie: the cell got higher)         */
  454.                     cutMore = YES;    /* Force cutting some more                */
  455.                                     /* Remove clipper string from front        */
  456.                     [temp removeFrom:0 length:[clipper length]];
  457.                 }            
  458.                 else {            /* Finished, string value can be displyed*/
  459.                     [temp free];
  460.                     FLUSH;
  461.                     return self;
  462.                 }
  463.             } // End else (check if string value can really be displayed)
  464.         } // End of string shortening loop
  465.     }//End if (clip on left)
  466.  
  467.     FLUSH;
  468.     return [super setStringValue:[temp stringValueAndFree]];
  469.     
  470. }
  471.  
  472. - _copyClipperObj:obj fromZone:(NXZone *)zone
  473. {
  474.     id newClipper = [obj copyFromZone:zone];
  475.  
  476.     clipper = newClipper;
  477.  
  478.     return self;
  479. }
  480.  
  481. - _copyDelimitersObj:obj fromZone:(NXZone *)zone
  482. {
  483.     id newDelim = [obj copyFromZone:zone];
  484.  
  485.     delimiters = newDelim;
  486.  
  487.     return self;
  488. }
  489.  
  490. - _copyFullStringObj:obj fromZone:(NXZone *)zone
  491. {
  492.     id newString = [obj copyFromZone:zone];
  493.  
  494.     fullString = newString;
  495.  
  496.     return self;
  497. }
  498.  
  499. @end
  500.  
  501.  
  502.  
  503. @implementation Object (MiscClipDelegate)
  504. - stringWillBeClipped:theString        { return self; }
  505. @end
  506.  
  507.  
  508. @implementation MiscClipTextFieldCell(IBStuff)
  509. - (const char *)getInspectorClassName
  510. // Return the class name of our inspector.
  511. {
  512.     return "MiscClipTextFieldInspector";
  513. }
  514. @end
  515.  
  516.  
  517.